<template>
  <dialog ref="dialogRef" class="dyn-attr-dialog" @close="onNativeClose">
    <form method="dialog" class="dialog-body" @submit.prevent="submit">
      <div class="dialog-header">
        <h3>属性设置 <small v-if="materialName || categoryName" class="cat">{{ materialName || categoryName }}</small></h3>
        <button type="button" class="close-x" @click="close" aria-label="关闭">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>
        </button>
      </div>
      <div v-if="definitions.length" class="grouped-wrapper">
        <div v-if="sizeDefs.length" class="group-block size-group" role="group" aria-label="尺寸">
          <header class="group-title"><span class="dot size"></span><h4>尺寸</h4></header>
          <div class="row size-row">
            <div v-for="def in sizeDefs" :key="def.code" class="field-block size field-card" :class="def.control.toLowerCase()">
              <label :for="def.code">{{ def.name }}<span v-if="def.required" class="req">*</span></label>
              <div v-if="def.control==='NumericTextBox'" class="num-wrap">
                <input
                  :id="def.code"
                  type="number"
                  :step="numericStep(def)"
                  :placeholder="ph(def)"
                  v-model="localValues[def.code]"
                  @blur="onNumericBlur(def)"
                />
                <span v-if="def.unit" class="unit">{{ def.unit }}</span>
              </div>
              <input v-else-if="def.control==='TextBox'" :id="def.code" type="text" :placeholder="ph(def)" v-model="localValues[def.code]" />
              <label v-else-if="def.control==='checkbox'" class="single-checkbox">
                <input type="checkbox" :id="def.code" v-model="localValues[def.code]" />
                <span class="chk-text">{{ localValues[def.code] ? '有' : '无' }}</span>
              </label>
              <select v-else :id="def.code" v-model="localValues[def.code]" @focus="maybeLoadOptions(def)"><option value="" disabled>{{ ph(def,true) }}</option><option v-for="o in dropdownOptions[def.code]" :key="o.value" :value="o.value">{{ o.label }}</option></select>
              <p v-if="loadingOptions[def.code]" class="loading-hint">加载...</p>
            </div>
          </div>
        </div>
        <div v-if="basicDefs.length" class="group-block basic-group" role="group" aria-label="基本信息">
          <header class="group-title"><span class="dot basic"></span><h4>基本信息</h4></header>
          <div class="grid basic-grid">
            <div v-for="def in basicDefs" :key="def.code" class="field-block basic field-card" :class="def.control.toLowerCase()">
              <label :for="def.code">{{ def.name }}<span v-if="def.required" class="req">*</span></label>
              <input v-if="def.control==='TextBox'" :id="def.code" type="text" :placeholder="ph(def)" v-model="localValues[def.code]" />
              <div v-else-if="def.control==='NumericTextBox'" class="num-wrap">
                <input
                  :id="def.code"
                  type="number"
                  :step="numericStep(def)"
                  :placeholder="ph(def)"
                  v-model="localValues[def.code]"
                  @blur="onNumericBlur(def)"
                />
                <span v-if="def.unit" class="unit">{{ def.unit }}</span>
              </div>
              <label v-else-if="def.control==='checkbox'" class="single-checkbox">
                <input type="checkbox" :id="def.code" v-model="localValues[def.code]" />
                <span class="chk-text">{{ localValues[def.code] ? '有' : '无' }}</span>
              </label>
              <select v-else :id="def.code" v-model="localValues[def.code]" @focus="maybeLoadOptions(def)"><option value="" disabled>{{ ph(def,true) }}</option><option v-for="o in dropdownOptions[def.code]" :key="o.value" :value="o.value">{{ o.label }}</option></select>
              <p v-if="loadingOptions[def.code]" class="loading-hint">加载...</p>
            </div>
          </div>
        </div>
        <div v-if="remarkDefs.length" class="group-block remark-group" role="group" aria-label="备注">
          <header class="group-title"><span class="dot remark"></span><h4>备注</h4></header>
          <div class="row remark-row">
            <div v-for="def in remarkDefs" :key="def.code" class="field-block remark-block field-card wide" :class="def.control.toLowerCase()">
              <label :for="def.code">{{ def.name }}</label>
              <textarea v-if="def.control==='TextBox'" :id="def.code" rows="3" :placeholder="ph(def)" v-model="localValues[def.code]"></textarea>
              <template v-else>
                <input v-if="def.control==='NumericTextBox'" :id="def.code" type="number" :step="numericStep(def)" :placeholder="ph(def)" v-model="localValues[def.code]" @blur="onNumericBlur(def)" />
                <label v-else-if="def.control==='checkbox'" class="single-checkbox">
                  <input type="checkbox" :id="def.code" v-model="localValues[def.code]" />
                  <span class="chk-text">{{ localValues[def.code] ? '有' : '无' }}</span>
                </label>
                <select v-else :id="def.code" v-model="localValues[def.code]" @focus="maybeLoadOptions(def)"><option value="" disabled>{{ ph(def,true) }}</option><option v-for="o in dropdownOptions[def.code]" :key="o.value" :value="o.value">{{ o.label }}</option></select>
              </template>
              <p v-if="loadingOptions[def.code]" class="loading-hint">加载...</p>
            </div>
          </div>
        </div>
      </div>
      <p v-else class="empty-def">该分类暂无可配置属性。</p>
      <div class="dialog-actions">
        <button type="button" class="btn ghost" @click="close">取消</button>
        <button type="submit" class="btn primary" :disabled="!isValid || submitting">保存</button>
      </div>
    </form>
  </dialog>
</template>

<script setup lang="ts">
import { ref, reactive, computed } from 'vue';
import type { MaterialAttributeDefinition, AttributeOptionItem } from '../mock/materialAttributes';
import { buildAttributeSpec } from '../mock/materialAttributes';

interface OpenPayload {
  categoryCode: string;
  categoryName: string; // 分类名称
  materialName?: string; // 物料名称（用于标题展示）
  definitions: MaterialAttributeDefinition[];
  values: Record<string, any>;
}

const dialogRef = ref<HTMLDialogElement | null>(null);
const definitions = ref<MaterialAttributeDefinition[]>([]);
const categoryName = ref('');
const materialName = ref('');
const categoryCode = ref('');
const localValues = reactive<Record<string, any>>({});
const dropdownOptions = reactive<Record<string, AttributeOptionItem[]>>({});
const loadingOptions = reactive<Record<string, boolean>>({});
const submitting = ref(false);

let resolveFn: ((data: { values: Record<string, any>; spec: string }) => void) | null = null;
let rejectFn: ((reason?: any) => void) | null = null;

async function open(payload: OpenPayload): Promise<{ values: Record<string, any>; spec: string } | undefined> {
  definitions.value = payload.definitions;
  categoryCode.value = payload.categoryCode;
  categoryName.value = payload.categoryName;
  materialName.value = payload.materialName || '';
  // 清空旧值
  Object.keys(localValues).forEach(k => delete (localValues as any)[k]);
  Object.keys(dropdownOptions).forEach(k => delete (dropdownOptions as any)[k]);
  Object.keys(loadingOptions).forEach(k => delete (loadingOptions as any)[k]);
  // 赋初值
  payload.definitions.forEach(d => {
    if(d.control === 'checkbox') {
      localValues[d.code] = payload.values?.[d.code] === true; // 默认为 false
    } else {
      localValues[d.code] = payload.values?.[d.code] ?? '';
    }
  });
  submitting.value = false;
  const el = dialogRef.value;
  if (el) {
    el.classList.remove('closing');
    el.showModal();
  }
  preloadDropdowns();
  return new Promise((resolve, reject) => { resolveFn = resolve; rejectFn = reject; });
}

function close() {
  const el = dialogRef.value; if (!el) return; if (el.classList.contains('closing')) return;
  el.classList.add('closing');
  const done = () => { el.removeEventListener('transitionend', done); el.classList.remove('closing'); el.close(); };
  el.addEventListener('transitionend', done); setTimeout(done, 320);
  if (rejectFn) rejectFn('cancel');
  resolveFn = null; rejectFn = null;
}

function onNativeClose() {
  if (resolveFn) resolveFn(undefined as any);
  resolveFn = null; rejectFn = null;
}

async function maybeLoadOptions(def: MaterialAttributeDefinition) {
  if (def.control !== 'DropDownList') return;
  if (dropdownOptions[def.code]?.length || loadingOptions[def.code]) return;
  loadingOptions[def.code] = true;
  try {
    if ((def as any).selectItems && Array.isArray((def as any).selectItems) && (def as any).selectItems.length) {
      dropdownOptions[def.code] = (def as any).selectItems.map((s: any) => ({ value: String(s.value ?? s.id ?? s.value), label: s.label ?? s.itemName }));
    } else {
      // 无后端 selectItems 时保持空，不再使用本地假数据。
      dropdownOptions[def.code] = [];
    }
  } finally { loadingOptions[def.code] = false; }
}

function preloadDropdowns() {
  const tasks = definitions.value.filter(d => d.control === 'DropDownList').map(d => maybeLoadOptions(d));
  if (tasks.length) Promise.allSettled(tasks);
}

const isValid = computed(() => definitions.value.every(def => !def.required || (localValues[def.code] !== '' && localValues[def.code] != null)));
const sizeDefs = computed(() => definitions.value.filter(d => (d as any).group === 'size' || /^(length|width|height|thickness)$/i.test(d.code)));
const remarkDefs = computed(() => definitions.value.filter(d => (d as any).group === 'remark' || /remark|memo|note/i.test(d.code)));
const basicDefs = computed(() => definitions.value.filter(d => !sizeDefs.value.includes(d) && !remarkDefs.value.includes(d)));

// 已去除规格预览条；若后续需要可恢复以上逻辑

function ph(def: MaterialAttributeDefinition, select = false) {
  if (def.placeholder) return def.placeholder;
  if (select) return '请选择' + def.name;
  return (def.control === 'TextBox' ? '请输入' : '输入') + def.name;
}

function submit() {
  if (!isValid.value) return;
  submitting.value = true;
  const spec = buildAttributeSpec(categoryCode.value, localValues, definitions.value);
  const el = dialogRef.value; if (!el) return;
  el.classList.add('closing');
  const done = () => { el.removeEventListener('transitionend', done); el.classList.remove('closing'); el.close(); };
  el.addEventListener('transitionend', done); setTimeout(done, 320);
  // checkbox 多选：将空字符串转换为 undefined；确保复选框值为数组
  const normalized: Record<string, any> = {};
  Object.keys(localValues).forEach(k => {
    const def = definitions.value.find(d => d.code === k);
    const val = localValues[k];
    if (def?.control === 'checkbox') {
      normalized[k] = !!val; // 布尔化
    } else if (val === '') {
      normalized[k] = undefined;
    } else {
      normalized[k] = val;
    }
  });
  if (resolveFn) resolveFn({ values: normalized, spec });
  resolveFn = null; rejectFn = null;
}

// 数字属性辅助：生成 step 值（如 decimalDigits=2 => 0.01），默认允许小数
function numericStep(def: MaterialAttributeDefinition) {
  const digits = (def as any).decimalDigits;
  if (typeof digits === 'number' && digits > 0) {
    return Number('1'.padEnd(digits + 2, '0')) ** -1; // 例如 digits=2 -> 0.01
  }
  return 'any'; // 允许任意小数
}

// blur 时根据 keepZero / decimalDigits 进行格式化与截断
function onNumericBlur(def: MaterialAttributeDefinition) {
  const code = def.code;
  let raw = localValues[code];
  if (raw === '' || raw == null) return;
  let num = Number(raw);
  if (!Number.isFinite(num)) { localValues[code] = ''; return; }
  const digits = (def as any).decimalDigits;
  if (typeof digits === 'number' && digits >= 0) {
    if ((def as any).keepZero) {
      localValues[code] = num.toFixed(digits);
    } else {
      // 去除多余 0，例如 12.340 -> 12.34 ; 12.000 -> 12
      localValues[code] = parseFloat(num.toFixed(digits)).toString();
    }
  } else {
    // 未指定 decimalDigits：保持原值（去除首尾空格）
    localValues[code] = String(raw).trim();
  }
}

defineExpose({ open, close });
</script>

<style scoped>
/* 基础弹窗布局 */
.dyn-attr-dialog {
  width: 780px;
  max-width: 92vw;
  border: none;
  padding: 0;
  border-radius: 14px;
  background: linear-gradient(145deg,#ffffff,#f4f7fb 65%,#eef3f9);
  box-shadow: 0 8px 28px -4px rgba(0,0,0,.18), 0 2px 6px rgba(0,0,0,.08);
  color: #1f2530;
  overflow: visible;
  /* 平滑的淡入效果 */
  opacity: 0;
  transform: scale(0.95);
  transition: opacity 0.2s ease-out, transform 0.2s ease-out;
}
.dyn-attr-dialog[open] { 
  opacity: 1;
  transform: scale(1);
}
.dyn-attr-dialog::backdrop { 
  background: rgba(30,37,48,.38); 
  backdrop-filter: blur(3px);
  animation: backdropFadeIn 0.2s ease-out;
}
@keyframes backdropFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
/* 关闭动画 */
.dyn-attr-dialog.closing {
  opacity: 0;
  transform: scale(0.95);
  transition: transform .25s ease-in, opacity .25s ease-in;
}

.dialog-body { display: flex; flex-direction: column; gap: 16px; padding: 20px 22px 18px; }
.dialog-header { display:flex; align-items:center; justify-content:space-between; }
.dialog-header h3 { margin:0; font-size:18px; font-weight:600; letter-spacing:.5px; display:flex; align-items:center; gap:8px; }
.dialog-header .cat { font-size:12px; font-weight:500; padding:2px 6px; background:#eef3ff; color:#3756d5; border-radius:10px; }
.close-x { cursor:pointer; background:none; border:none; color:#5a6475; width:32px; height:32px; border-radius:50%; display:grid; place-items:center; transition:background .18s,color .18s; }
.close-x:hover { background:#e8edf5; color:#202632; }


/* 分组包装 */
.grouped-wrapper { display:flex; flex-direction:column; gap:18px; }
.group-block { background:#fff; border:1px solid #dfe4ec; border-radius:14px; padding:14px 16px 12px; position:relative; box-shadow:0 1px 3px rgba(0,0,0,.05); }
.group-block + .group-block { margin-top:2px; }
.group-title { display:flex; align-items:center; gap:8px; margin:-4px 0 10px; }
.group-title h4 { margin:0; font-size:14px; font-weight:600; letter-spacing:.5px; }
.dot { width:10px; height:10px; border-radius:50%; display:inline-block; box-shadow:0 0 0 3px rgba(0,0,0,.04) inset; }
.dot.size { background:#ffb347; }
.dot.basic { background:#6aa9ff; }
.dot.remark { background:#9f7aea; }

/* 字段卡片 */
.field-card { background:#f9fbfe; border:1px solid #e2e9f2; border-radius:10px; padding:10px 12px 12px; display:flex; flex-direction:column; gap:6px; position:relative; transition:background .25s, box-shadow .25s, border-color .25s; }
.field-card:hover { background:#fff; border-color:#c9d6e4; box-shadow:0 2px 4px -1px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.06); }
.field-card label { font-size:12px; font-weight:600; letter-spacing:.3px; color:#2d3645; display:flex; align-items:center; gap:4px; }
.field-card .req { color:#e54848; font-weight:600; }
.field-card input, .field-card select, .field-card textarea { font:inherit; font-size:13px; padding:6px 8px; border:1px solid #cfd8e4; border-radius:6px; background:#fff; color:#1d232c; outline:none; transition:border-color .18s, box-shadow .18s, background .18s; width:100%; }
.field-card input:focus, .field-card select:focus, .field-card textarea:focus { border-color:#4d83ff; box-shadow:0 0 0 3px rgba(80,140,255,.25); }
.field-card textarea { resize:vertical; min-height:86px; }
.checkbox-group { display:flex; flex-direction:column; gap:6px; padding:4px 2px; }
.chk-item { font-size:12px; font-weight:500; display:flex; align-items:center; gap:6px; }
.chk-item input[type='checkbox'] { width:14px; height:14px; }
.empty-opts { font-size:11px; color:#6f7a89; margin:0; }
.field-card select:disabled { opacity:.6; }
.loading-hint { font-size:11px; margin:0; color:#6f7a89; }

/* 数字输入带单位 */
.num-wrap { display:flex; align-items:center; gap:4px; }
.num-wrap input { flex:1; }
.num-wrap .unit { font-size:12px; padding:4px 6px; background:#eef3fa; border:1px solid #d4dce6; border-radius:5px; color:#3d4854; }

/* 布局栅格 */
.size-row { display:flex; gap:14px; flex-wrap:wrap; }
.size-row .field-card { flex:1 1 120px; min-width:160px; }
.basic-grid { display:grid; gap:14px; grid-template-columns:repeat(auto-fill,minmax(170px,1fr)); }
.remark-row { display:flex; flex-direction:column; gap:14px; }
.remark-row .wide { width:100%; }

/* 按钮区域 */
.dialog-actions { display:flex; justify-content:flex-end; gap:12px; margin-top:4px; }
.btn { cursor:pointer; border:none; border-radius:8px; padding:8px 22px; font-weight:600; font-size:14px; letter-spacing:.4px; display:inline-flex; align-items:center; gap:6px; background:#e9eef5; color:#2a333f; transition:background .2s, transform .2s, box-shadow .2s; }
.btn:hover { background:#dde4ec; }
.btn:active { transform:translateY(1px); }
.btn.primary { background:#4d83ff; color:#fff; box-shadow:0 4px 12px -3px rgba(77,131,255,.6),0 2px 4px rgba(0,0,0,.15); }
.btn.primary:hover { background:#3f74f5; }
.btn.primary:disabled { background:#9fbaf1; box-shadow:none; cursor:not-allowed; }
.btn.ghost { background:transparent; color:#3a4858; border:1px solid #bfc9d6; }
.btn.ghost:hover { background:#f1f5f9; }

/* 去除任何可能出现的滚动条视觉（若内容溢出由外部容器处理） */
.dyn-attr-dialog::-webkit-scrollbar { width:0; height:0; }
.group-block::-webkit-scrollbar, .dialog-body::-webkit-scrollbar { width:0; height:0; }
.group-block { scrollbar-width:none; }
.dialog-body { scrollbar-width:none; }

/* 深色模式微调（用户若启系统深色） */

@media (max-width: 640px) {
  .dyn-attr-dialog { width: 96vw; }
  .dialog-body { padding:16px 16px 14px; }
  .basic-grid { grid-template-columns:repeat(auto-fill,minmax(140px,1fr)); }
  .size-row .field-card { min-width:130px; }
}
</style>
